using Engine.FileFormats3D.C11Syndikat;
using Engine.FileFormats3D.C11Syndikat.QADFile;
using Engine.Graphics;
using Engine.Graphics.VertexStructures;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Engine.FileFormats3D
{
    public class C11SyndicatScenery : IRenderableObject
    {
        #region Public Fields

        public ShaderManager shaderManager;

        #endregion Public Fields

        #region Private Fields

        private Engine engine;
        private GeoFile geoFile;
        private MaterialManager materialManager;
        private Meshobject[] meshBlocks;
        private Mesh3dData[] meshes;
        private QadFile qadFile;
        private SceneManagement sceneManagement;

        #endregion Private Fields

        #region Public Constructors

        public C11SyndicatScenery(Engine engine, ShaderManager shaderManager, string scenerieFolder, Camera.Camera camera, float viewDistance)
        {
            this.shaderManager = shaderManager;
            this.engine = engine;
            this.engine.Grafic.AddRenderableObjects(this, 10);
            this.engine.AddShaderManager(this.shaderManager);
            string fileName = scenerieFolder + "\\V1\\" + GetSceneryName(scenerieFolder);
            this.qadFile = QadFile.FromFile(fileName + ".qad");
            this.geoFile = GeoFile.FromFile(fileName + ".geo", this.qadFile);

            //Create the MaterialManager.
            List<string> texturePaths = new List<string>();
            foreach (var textureName in qadFile.TextureNames)
            {
                texturePaths.Add(scenerieFolder + "\\V1\\Textures\\" + textureName.ToString() + ".ptx");
            }

            //Add the Wr2 Surfaces to the Material Manager
            materialManager = new MaterialManager(this.engine, texturePaths);
            foreach (var surface in qadFile.Surfaces)
            {
                materialManager.AddNewMaterial(surface.ToMaterial(materialManager), false);
            }

            this.sceneManagement = new SceneManagement(materialManager, shaderManager, camera, viewDistance);
            CreateMeshes(geoFile, qadFile);
            //CreateRenderMeshObjects();
        }

        #endregion Public Constructors

        #region Public Methods

        public void Dispose()
        {
            this.engine.Grafic.RemoveRenderableObject(this);
            for (int i = 0; i < this.meshBlocks.Length; i++)
            {
                this.meshBlocks[i].Dispose();
            }

            this.materialManager.Dispose();
            this.sceneManagement.Dispose();
        }

        public int GetPriorityLevel()
        {
            return 50;
        }

        void IRenderableObject.Render(int renderStep)
        {
            this.sceneManagement.Render(renderStep);
        }

        #endregion Public Methods

        #region Private Methods

        private static string GetSceneryName(string scenerieFolder)
        {
            // Split the Path to get the Scenery Name
            string[] temp = scenerieFolder.Split('\\');
            return (temp[temp.Length - 1]);
        }

        private void CreateMeshes(GeoFile geoFile, QadFile qadFile)
        {
            this.meshes = new Mesh3dData[qadFile.CellsX * qadFile.CellsY];

            Dictionary<MyOwnVertex.MyPositionNormalTextured, ushort> vertexDirectory = new Dictionary<MyOwnVertex.MyPositionNormalTextured, ushort>();

            this.meshBlocks = new Meshobject[qadFile.CellsX * qadFile.CellsY];

            for (int b = 0; b < qadFile.BlockInfos.Length; b++)
            {
                BlockInfo blockInfo = qadFile.BlockInfos[b];
                C11VertexBlock vertexBlock = geoFile.VertexBlocks[blockInfo.Chunk65K];

                List<ushort> indices = new List<ushort>();
                List<MyOwnVertex.MyPositionNormalTextured> vertices = new List<MyOwnVertex.MyPositionNormalTextured>();

                vertexDirectory.Clear();

                List<int> indexedMaterilID = new List<int>();
                List<Graphics.Material> materials = new List<Graphics.Material>();
                List<int> numberOfUsedPolysPerMaterial = new List<int>();

                for (int s = 0; s < blockInfo.NumberOfSurfaceChunks; s++)
                {
                    int surfaceblock = s + (int)blockInfo.FirstSurfaceChunk;
                    SurfaceChunk surfaceChunk = qadFile.SurfaceChunks[surfaceblock];

                    // Add the material ID.
                    indexedMaterilID.Add((int)surfaceChunk.surface);
                    //materials.Add(new Graphics.Material(qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture1ID].ToString(),
                    //    qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture2ID].ToString(),
                    //    qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture3ID].ToString(),
                    //    (EnumMappingMode)qadFile.Surfaces[(int)surfaceChunk.surface].mappingMode,
                    //    qadFile.Surfaces[(int)surfaceChunk.surface].mappings1,
                    //    qadFile.Surfaces[(int)surfaceChunk.surface].mappings2,
                    //    qadFile.Surfaces[(int)surfaceChunk.surface].mappings3));
                    materials.Add(new Graphics.Material(qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture1ID].ToString(),
                        qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture2ID].ToString(),
                        qadFile.TextureNames[(int)qadFile.Surfaces[(int)surfaceChunk.surface].texture3ID].ToString(),
                        (EnumMappingMode)qadFile.Surfaces[(int)surfaceChunk.surface].mappingMode));

                    // Seek the indices and add the vertices to the array.
                    numberOfUsedPolysPerMaterial.Add((int)surfaceChunk.numberOfPolygons);
                    for (int p = 0; p < surfaceChunk.numberOfPolygons * 3; p++)
                    {
                        int indice = (int)geoFile.AllIndices[p + (int)surfaceChunk.startPolygon * 3];

                        MyOwnVertex.MyPositionNormalTextured vertex = vertexBlock.EngineVertices[indice];
                        // search the vertex in the array and add the indice to the other array.
                        ushort index = 0;
                        if (vertexDirectory.TryGetValue(vertex, out index))
                        {
                            indices.Add(index);
                        }
                        else
                        {
                            indices.Add((ushort)vertices.Count);
                            vertexDirectory.Add(vertex, (ushort)vertices.Count);
                            vertices.Add(vertex);
                        }
                    }
                }
                this.meshes[b] = new Mesh3dData(indices, vertices, numberOfUsedPolysPerMaterial, materials);

                this.meshBlocks[b] = new Meshobject(this.engine.Device, this.materialManager, this.shaderManager, this.meshes[b]);
                this.sceneManagement.AddMeshObject(this.meshBlocks[b]);

                //this.engine.Render();
                //this.engine.Update();
                //if (b % 2 == 0)
                //    this.sceneManagement.RemoveMeshObject(this.meshBlocks[b]);
                Application.DoEvents();
            }
            //MessageBox.Show("double vertice found: " + MyOwnVertex.MyPositionNormalTextured.doubleVerticesFound.ToString());
        }

        private void CreateRenderMeshObjects()
        {
            this.meshBlocks = new Meshobject[qadFile.CellsX * qadFile.CellsY];

            for (int mb = 0; mb < this.meshes.Length; mb++)
            {
                this.meshBlocks[mb] = new Meshobject(this.engine.Device, this.materialManager, this.shaderManager, this.meshes[mb]);
                this.sceneManagement.AddMeshObject(this.meshBlocks[mb]);
                //this.engine.Render();
                //if (mb % 2 == 0)
                //    this.sceneManagement.RemoveMeshObject(this.meshBlocks[mb]);
                //Application.DoEvents();
            }
        }

        #endregion Private Methods
    }
}